package org.eclipse.cargotracker.domain.model.cargo;

import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.eclipse.cargotracker.domain.model.handling.HandlingEvent;
import org.eclipse.cargotracker.domain.model.location.Location;
import org.eclipse.cargotracker.domain.model.voyage.Voyage;

import javax.persistence.*;
import java.io.Serializable;

/**
 * A handling activity represents how and where a cargo can be handled, and can be used to express
 * predictions about what is expected to happen to a cargo in the future.
 */
@Embeddable
public class HandlingActivity implements Serializable {

    public static final HandlingActivity EMPTY = new HandlingActivity();
    private static final long serialVersionUID = 1L;

    @Enumerated(EnumType.STRING)
    @Column(name = "next_expected_handling_event_type")
    @NotNull(message = "Handling event type is required.")
    private HandlingEvent.Type type;

    @ManyToOne
    @JoinColumn(name = "next_expected_location_id")
    @NotNull(message = "Location is required.")
    private Location location;

    @ManyToOne
    @JoinColumn(name = "next_expected_voyage_id")
    private Voyage voyage;

    public HandlingActivity() {}

    public HandlingActivity(HandlingEvent.Type type, Location location) {
        Validate.notNull(type, "Handling event type is required");
        Validate.notNull(location, "Location is required");

        this.type = type;
        this.location = location;
    }

    public HandlingActivity(HandlingEvent.Type type, Location location, Voyage voyage) {
        Validate.notNull(type, "Handling event type is required");
        Validate.notNull(location, "Location is required");
        Validate.notNull(voyage, "Voyage is required");

        this.type = type;
        this.location = location;
        this.voyage = voyage;
    }

    public HandlingEvent.Type getType() {
        return type;
    }

    public Location getLocation() {
        return location;
    }

    public Voyage getVoyage() {
        return voyage;
    }

    private boolean sameValueAs(HandlingActivity other) {
        return other != null
                && new EqualsBuilder()
                        .append(this.type, other.type)
                        .append(this.location, other.location)
                        .append(this.voyage, other.voyage)
                        .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder()
                .append(this.type)
                .append(this.location)
                .append(this.voyage)
                .toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (obj.getClass() != this.getClass()) {
            return false;
        }

        HandlingActivity other = (HandlingActivity) obj;

        return sameValueAs(other);
    }

    public boolean isEmpty() {
        if (type != null) {
            return false;
        }

        if (location != null) {
            return false;
        }

        return voyage == null;
    }

    @Override
    public String toString() {
        return "HandlingActivity{"
                + "type="
                + type
                + ", location="
                + location
                + ", voyage="
                + voyage
                + '}';
    }
}
